/**
 *
 * Copyright (C) 2007 iPhone Dev Team  
 *
 *    Unlock Authors: Daeken, gray, iZsh, Sam
 *    UI Development: Erica, kroo, darkten
 *	  RCE: roxfan, daeken, darkmen, gray, iZsh, Sam  
 *    Coop: pytey, uns, zappaz, Zf, guest184, leachbj
 *	  Art: Wheat, darkten
 *
 *    Paypal donation and contact -> iphone.devteam@gmail.com
 *
 *	  For further information check http://www.hackint0sh.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/* Old Header:

  iUnlock v4243.AutoSpeedy -- Copyright 2007 The dev team

  Credits: Daeken, Darkmen, guest184, gray, iZsh, leachbj, pytey, roxfan, Sam, uns, zappaz, Zf
  Paypal donation to iphone.devteam@gmail.com
 
  All code, information or data [from now on "data"] available
  from the "iPhone dev team" [1] or any other project linked from
  this or other pages is owned by the creator who created the data.
  The copyright, license right, distribution right and any other
  rights lies with the creator.
  
  It is prohibitied to use the data without the written agreement
  of the creator. This included using ideas in other projects
  (commercial or not commercial).
  
  Where data was created by more than 1 creator a written agreement
  from each of the creators has to be obtained.

  Punishment: Monkeys coming out of your ass Bruce Almighty style.

  [1] http://iphone.fiveforty.net/wiki/index.php?title=Main_Page
  
  anySIM v1.1 changes - darkten
 
  anySIM v1.1.2 changes - netkas, drudge
*/

#import <CoreFoundation/CoreFoundation.h>
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <GraphicsServices/GraphicsServices.h>
#import <IOKit/IOKitLib.h>
#ifndef kIOPMAssertionTypeNoIdleSleep
#define kIOPMAssertionTypeNoIdleSleep                 CFSTR("NoIdleSleepAssertion")
#endif

// other imports -- should be part of UIKit:
#import <UIKit/UIBox.h>
#import <UIKit/UIView-Geometry.h>
#import <UIKit/UIAnimator.h>
#import <UIKit/UICheckboxImage.h>
#import <UIKit/UITransformAnimation.h>
#import <UIKit/UIRotationAnimation.h>
#import <UIKit/UIAlphaAnimation.h>
#import <UIKit/UIBezierPath.h>
#import <UIKit/UIButtonBar.h>
#import <UIKit/UINavigationBar.h>
#import <UIKit/UIButtonBarButton.h>
#import "Unlock.h"

id thisapp;
id mainV;
id navB;
id mrAlert;
id passthruAlert;
BOOL restartComm = NO;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <time.h>


#include "packets.h"

#define ever ;;
#define LOG stdout
#define SPEED 750000

#pragma pack(1)

#define BUFSIZE (65536+100)
unsigned char readbuf[BUFSIZE];

struct termios term;

//#define DEBUG_ENABLED 1

#ifndef DEBUG_ENABLED
#define DEBUGLOG(x) 
#else
#define DEBUGLOG(x) x
#endif

#define UINT(x) *((unsigned int *) (x))

#define FW31206 "DEV_ICE_MODEM_03.12.06_G"
#define FW31408 "DEV_ICE_MODEM_03.14.08_G"
#define FW40113 "DEV_ICE_MODEM_04.01.13_G"
#define FW40213 "DEV_ICE_MODEM_04.02.13_G"

#define SECPACK31206 "/Applications/anySIM.app/secpack31206.bin"
#define SECPACK31408 "/Applications/anySIM.app/secpack31408.bin"
#define SECPACK40113 "/Applications/anySIM.app/secpack40113.bin"
#define SECPACK40213 "/Applications/anySIM.app/secpack40213.bin"

#define ATINIT_CMD "AT\r"
#define UNLOCK_CMD "AT+CLCK=\"PN\",0,\"00000000\"\r"
#define UNLOCKTEST_CMD "AT+CLCK=\"PN\",2\r"
#define UNLOCKTEST_OK "+CLCK: 0"
#define LAUNCHCTL_STOP "/bin/launchctl unload -w /System/Library/LaunchDaemons/com.apple.CommCenter.plist"
#define LAUNCHCTL_START "/bin/launchctl load -w /System/Library/LaunchDaemons/com.apple.CommCenter.plist"
#define NOR_FILENAME "/tmp/nor.bin"

const char * RE = "Why the hell are you reversing this app?! We said we were "\
"going to release the sources...";

#include <objc/objc.h>
#include <objc/objc-runtime.h>

double objc_msgSend_fpret(id self, SEL op, ...) 
{
   Method method = class_getInstanceMethod(self->isa, op);
   int numArgs = method_getNumberOfArguments(method);

   if(numArgs == 2) {
        double (*imp)(id, SEL);
        imp = (double (*)(id, SEL))method->method_imp;
        return imp(self, op);
    } else if(numArgs == 3) {
         va_list ap;
         va_start(ap, op);
         double (*imp)(id, SEL, void *);
         imp = (double (*)(id, SEL, void *))method->method_imp;
         return imp(self, op, va_arg(ap, void *));
    }

    fprintf(stderr, 
       "ERROR: objc_msgSend_fpret called on <%s %p> with selector %s had to return 0.0\n", object_getClassName(self), 
        self, sel_getName(op));
    return 0.0;
}



@implementation Unlock

- (void) applicationDidFinishLaunching: (id) unused {
    window = [[UIWindow alloc] initWithContentRect: [UIHardware
  						    fullScreenApplicationContentRect]];

    [window orderFront: self];
    [window makeKey: self];
    [window _setHidden: NO];

	thisapp = self;
	
    struct CGRect rect = [UIHardware fullScreenApplicationContentRect];
    rect.origin.x = rect.origin.y = 0.0f;

    theProgress = [[UIProgressBar alloc] initWithFrame:CGRectMake(30.0f, 55.0f, 260.0f, 30.0f)];
    //[theProgress setProgress:0.0];
    progressWindow = [[UIAlertSheet alloc ] init];
    [progressWindow setTitle:@"Ready to Unlock"];
    [progressWindow setDelegate:self];
    [progressWindow setContext:self];
    [progressWindow addSubview:theProgress];
    [progressWindow setDimsBackground:YES];
	[progressWindow setRunsModal:YES];
	[progressWindow setAlertSheetStyle:1];
	[progressWindow setNumberOfRows:1];
    [progressWindow setTableShouldShowMinimumContent:YES];
    [progressWindow setBlocksInteraction:NO];
    [progressWindow _slideSheetOut:YES];
    [progressWindow layoutAnimated:YES];

    SliderControl *slider = [[SliderControl alloc] init];
    [slider registerCallback:@selector(showReadme) forId: self];
	
	navBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0f, 460.0f, 320.0f, 20.0f)];
	[navBar setBarStyle:1];
	
	navB = navBar;
	
    UIImage *bgimage = [[UIImage alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource :@"background" ofType:@"png"]];
    [bgimage retain];
    UIImageView *bgimagev = [[UIImageView alloc] initWithImage: bgimage];
    [bgimagev setFrame: rect];


    UIView *mainView;
    mainView = [[UIView alloc] initWithFrame: rect];
    [mainView addSubview: bgimagev];
    [mainView addSubview: slider];
	[mainView addSubview: navBar];
    [window setContentView: mainView];
	
	mainV = mainView;
	
	BOOL goodToGo = NO;
	
	NSFileManager *mandingo = [NSFileManager defaultManager];
	if([mandingo fileExistsAtPath:@"/var/root/Library/Preferences/com.apple.springboard.plist"]){
	NSDictionary *springy = [NSDictionary dictionaryWithContentsOfFile:@"/var/root/Library/Preferences/com.apple.springboard.plist"];
	if([[springy valueForKey:@"SBAutoLockTime"] intValue] != -1){ 
	[self exitUnlockWithErrorMessage:@"You must set Auto-Lock to \"Never\" in Settings (General/Auto-Lock) to continue. anySIM will now quit."];
	}else{
	goodToGo = YES;
	}
	}
	
	if(goodToGo){
	NSMutableArray *click = [[NSMutableArray alloc] init];
				[click addObject:[NSString stringWithString:@"OK"]];
								
				UIAlertSheet *welcome = [[UIAlertSheet alloc] 
							initWithTitle:@"Welcome to anySIM v1.1.2"
							buttons:click
							defaultButtonIndex:0
							delegate:self
							context:self];
				[welcome setBodyText:@"To get started, Slide to Unlock, Read our License, scroll down and press the red button to begin. \n\nPlease make sure your new\nSIM is in your iPhone."];
				[welcome setDimsBackground:YES];
				[welcome _slideSheetOut:YES];
				[welcome setRunsModal:YES];
				[welcome setShowsOverSpringBoardAlerts:YES];
				passthruAlert = welcome;
				[welcome popupAlertAnimated:YES];

	}	
	
	
}

- (void) buttonEvent:(UIPushButton *)button {
   [window setContentView:mainV];
   [progressWindow presentSheetFromAboveView: [window contentView]];
    StopCommcenter();
	sleep(2);
  [NSThread detachNewThreadSelector:@selector(doUnlock:) toTarget:self withObject:nil]; 
}

-(void)showReadme {
  UIScroller *scroller = [[UIScroller alloc] initWithFrame: CGRectMake(0.0f, 0.f, 320.0f,480.f)];
  
  UIImage *image = [[UIImage alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource :@"strip" ofType:@"png"]];
  [image retain];
  UIImageView *imagev = [[UIImageView alloc] initWithImage: image];
  [imagev setFrame: CGRectMake(0, 0, 320, 828)];
  
  //Button images - in application bundle.  These are from the clock app
  UIImage* btnImage = [[UIImage alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource :@"UIPreferencesDeleteButtonNormal" ofType:@"png"]];
  UIImage* btnImagePressed = [[UIImage alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource :@"UIPreferencesDeleteButtonPressed" ofType:@"png"]];
  
  UIPushButton *button = [[UIPushButton alloc] initWithTitle: @"OK, Unlock My Phone" autosizesToFit: NO];
  [button setFrame: CGRectMake(5, 828, 310, 45)];
  [button setDrawsShadow: YES];
  [button setStretchBackground:YES];
  [button setBackground:btnImage forState:0];  //up state
  [button setBackground:btnImagePressed forState:1]; //down state
  [button addTarget:self action:@selector(buttonEvent:) forEvents:1];

  [scroller setContentSize: CGSizeMake(320, 875)];  
  [scroller addSubview:imagev];
  [scroller addSubview:button];
  
  [window setContentView: scroller];
}


-(void)setProgress:(NSArray *)arg {
  double percentage = [[arg objectAtIndex:0]floatValue] / 100.0;
  [theProgress setProgress:percentage];
  [progressWindow setTitle:[arg objectAtIndex:1]];
}

-(void)unlockDone{

[progressWindow dismissAnimated:YES];
NSMutableArray *buttons = [[NSMutableArray alloc] init];
				[buttons addObject:[NSString stringWithString:@"OK"]];
								
				UIAlertSheet *sheet = [[UIAlertSheet alloc] 
							initWithTitle:@"Unlock Sucessful!"
							buttons:buttons
							defaultButtonIndex:0
							delegate:self
							context:self];
				[sheet setBodyText:@"Your iPhone has been successfully unlocked."];
				[sheet setDimsBackground:YES];
				[sheet _slideSheetOut:YES];
				[sheet setRunsModal:YES];
				[sheet setAlertSheetStyle:1];
				[sheet setShowsOverSpringBoardAlerts:YES];
				mrAlert = sheet;
				//[sheet popupAlertAnimated:YES];
				[sheet presentSheetFromAboveView: [window contentView]];
}



- (void) alertSheet:(id)aSheet buttonClicked:(int) bnum
{  
if(aSheet = mrAlert){

[self terminate];
}
if(aSheet = passthruAlert)
[passthruAlert dismissAnimated:YES];


}


-(void)exitUnlockWithErrorMessage:(NSString *)awDang{
NSString *theTitle = [@"Unlock Failure" retain];
if(restartComm)
theTitle = @"Please Restart your iPhone";
//StartCommcenter();

[progressWindow dismissAnimated:YES];
NSMutableArray *buttons = [[NSMutableArray alloc] init];
				[buttons addObject:[NSString stringWithString:@"OK"]];
								
				UIAlertSheet *sheet = [[UIAlertSheet alloc] 
							initWithTitle:@"Please Restart your iPhone"
							buttons:buttons
							defaultButtonIndex:0
							delegate:self
							context:self];
				[sheet setBodyText:awDang];
				[sheet setDimsBackground:YES];
				[sheet _slideSheetOut:YES];
				[sheet setRunsModal:YES];
				[sheet setAlertSheetStyle:1];
				[sheet setShowsOverSpringBoardAlerts:YES];
				mrAlert = sheet;
				//[sheet popupAlertAnimated:YES];
				[sheet presentSheetFromAboveView: [window contentView]];
}

-(void)applicationWillTerminate{
if(restartComm){
FILE *fp = popen(LAUNCHCTL_START, "r");
  if (fp == NULL) {
    [thisapp exitUnlockWithErrorMessage: [NSString stringWithFormat: @"Error while executing %s", @LAUNCHCTL_START]];
  }

  int len = ReadResp(fileno(fp));
  readbuf[len] = 0; // Buffer is big enough to allow this
  DEBUGLOG(printf("CommCenter: %s\n", readbuf));
  fclose(fp);
  sleep(1);
  //[thisapp setProgress:100.0 withMessage:@"Starting the CommCenter"];
 // [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0],@"Starting the CommCenter", nil] waitUntilDone:NO];
}

}

void StartCommcenter()
{
  //[thisapp setProgress:0.0 withMessage:@"Starting the CommCenter"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],@"Starting the CommCenter", nil] waitUntilDone:NO];
  FILE *fp = popen(LAUNCHCTL_START, "r");
  if (fp == NULL) {
    [thisapp exitUnlockWithErrorMessage: [NSString stringWithFormat: @"Error while executing %s", @LAUNCHCTL_START]];
  }

  int len = ReadResp(fileno(fp));
  readbuf[len] = 0; // Buffer is big enough to allow this
  DEBUGLOG(printf("CommCenter: %s\n", readbuf));
  fclose(fp);
  sleep(1);
  //[thisapp setProgress:100.0 withMessage:@"Starting the CommCenter"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0],@"Starting the CommCenter", nil] waitUntilDone:NO];
}


void HexDumpLine(unsigned char *buf, int remainder, int offset)
{
  int i = 0;
  char c = 0;

  // Print the hex part
  fprintf(LOG, "%08x | ", offset);
  for (i = 0; i < 16; ++i) {
    if (i < remainder)
      fprintf(LOG, "%02x%s", buf[i], (i == 7) ? "  " : " ");
    else
      fprintf(LOG, "  %s", (i == 7) ? "  " : " ");
  }
  // Print the ascii part
  fprintf(LOG, " | ");
  for (i = 0; i < 16 && i < remainder; ++i) {
    c = buf[i];
    if (c >= 0x20 && c <= 0x7e)
      fprintf(LOG, "%c%s", c, (i == 7) ? " " : "");
    else
      fprintf(LOG, ".%s", (i == 7) ? " " : "");
  }

  fprintf(LOG, "\n");
}

void HexDump(unsigned char *buf, int size)
{
  int i = 0;

  for (i = 0; i < size; i += 16)
    HexDumpLine(buf + i, size - i, i);
  fprintf(LOG, "%08x\n", size);
}

int Checksum(CmdHeader * packet)
{
  int sum = 0x00030000;
  sum += packet->opcode;
  sum += packet->param_len;

  int len = packet->param_len;
  unsigned char * buf = ((unsigned char *)packet) + sizeof (CmdHeader);
  int i = 0;  

  for (i = 0; i < len; ++i)
    sum += buf[i];
  return sum;
}

void SendCmd(int fd, void *buf, size_t size)
{
  DEBUGLOG(fprintf(LOG, "Sending:\n"));
  DEBUGLOG(HexDump((unsigned char*)buf, size));

  if(write(fd, buf, size) == -1) {
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"Error while sending command" waitUntilDone:NO];
  }
}

#define sendBytes(fd, args...) {\
  unsigned char sendbuf[] = {args}; \
  SendCmd(fd, sendbuf, sizeof(sendbuf)); \
}

int ReadResp(int fd)
{
  int len = 0;
  struct timeval timeout;
  int nfds = fd + 1;
  fd_set readfds;

  FD_ZERO(&readfds);
  FD_SET(fd, &readfds);

  // Wait a second
  timeout.tv_sec = 0;
  timeout.tv_usec = 50000;

  // Read data
  while (select(nfds, &readfds, NULL, NULL, &timeout) > 0)
    len += read(fd, readbuf + len, BUFSIZE - len);

  if (len > 0) {
    DEBUGLOG(fprintf(LOG, "Read:\n"));
    DEBUGLOG(HexDump(readbuf, len));
  }
  return len;
}

int InitConn(int speed)
{
  //  int fd = open("/dev/tty.baseband", O_RDWR | 0x20000 | O_NOCTTY);
  int fd = open("/dev/tty.baseband", 0x20002);

  unsigned int blahnull = 0;
  unsigned int handshake = TIOCM_DTR | TIOCM_RTS | TIOCM_CTS | TIOCM_DSR;

  if(fd == -1) {
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"Error opening the baseband" waitUntilDone:NO];
  }

  ioctl(fd, 0x2000740D);
  fcntl(fd, 4, 0);
  tcgetattr(fd, &term);

  ioctl(fd, 0x8004540A, &blahnull);
  cfsetspeed(&term, speed);
  cfmakeraw(&term);
  term.c_cc[VMIN] = 0;
  term.c_cc[VTIME] = 5;

  term.c_iflag = (term.c_iflag & 0xFFFFF0CD) | 5;
  term.c_oflag =  term.c_oflag & 0xFFFFFFFE;
  term.c_cflag = (term.c_cflag & 0xFFFC6CFF) | 0x3CB00;
  term.c_lflag =  term.c_lflag & 0xFFFFFA77;

  term.c_cflag = (term.c_cflag & ~CSIZE) | CS8;
  term.c_cflag &= ~PARENB;
  term.c_lflag &= ~ECHO;

  tcsetattr(fd, TCSANOW, &term);

  ioctl(fd, TIOCSDTR);
  ioctl(fd, TIOCCDTR);
  ioctl(fd, TIOCMSET, &handshake);

  return fd;
}

void RestartBaseband()
{
  kern_return_t   result;
  mach_port_t     masterPort;

  result = IOMasterPort(MACH_PORT_NULL, &masterPort);
  if (result) {
    DEBUGLOG(printf("IOMasterPort failed\n"));
    return;
  }

  CFMutableDictionaryRef matchingDict = IOServiceMatching("AppleBaseband");  
  io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);
  if (!service) {
    DEBUGLOG(printf("IOServiceGetMatchingService failed\n"));
    return;
  }

  io_connect_t conn;
  result = IOServiceOpen(service, mach_task_self(), 0, &conn);
  if (result) {
    DEBUGLOG(printf("IOServiceOpen failed\n"));
    return;
  }

  result = IOConnectCallScalarMethod(conn, 0, 0, 0, 0, 0);
  if (result == 0)
    DEBUGLOG(printf("Baseband reset.\n"));
  else
    DEBUGLOG(printf("Baseband reset failed\n"));
  IOServiceClose(conn);
}

void SetBaudRate(int fd, unsigned int baudrate)
{
  //[thisapp setProgress:0.0 withMessage: [NSString stringWithFormat: @"Sending baudrate command speed %d", baudrate]];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0], [NSString stringWithFormat: @"Sending baudrate command speed %d", baudrate], nil] waitUntilDone:NO];
  BaudReq req;
  req.cmd.cls = 0x2;
  req.cmd.opcode = BBSETBAUDRATE;
  req.cmd.param_len = 0x4;
  req.baud = baudrate;
  req.checksum = Checksum((CmdHeader*)&req);
  SendCmd(fd, &req, sizeof (SeekReq));
  DEBUGLOG(printf("Reading answer\n"));
  ReadResp(fd);

  tcgetattr(fd, &term);
  cfsetspeed(&term, baudrate);
  tcsetattr(fd, TCSANOW, &term);
  //[thisapp setProgress:100.0 withMessage: [NSString stringWithFormat: @"Sending baudrate command speed %d", baudrate]];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0], [NSString stringWithFormat: @"Sending baudrate command speed %d", baudrate], nil] waitUntilDone:NO];
}

void SendAT(int fd)
{
  SendCmd(fd, ATINIT_CMD, strlen(ATINIT_CMD));
}

void InitAT(int fd)
{
  SendAT(fd);
  for (ever) {
    if(ReadResp(fd) != 0)
      break;
    SendAT(fd);
  }
}


void SendGetVersion(int fd)
{
  sendBytes(fd, 0x60, 0x0D);
}

void GetVersion(int fd)
{
  SendGetVersion(fd);
  for (ever) {
    if(ReadResp(fd) != 0) {
      if(readbuf[0] == 0x0b)
        break;
    }
    SendGetVersion(fd);
  }

  VersionAck *ver = (VersionAck *) readbuf;
  DEBUGLOG(printf("Boot mode: %02X\n", ver->bootmode));
  DEBUGLOG(printf("Major: %d, Minor: %d\n", ver->major, ver->minor));
  DEBUGLOG(printf("Version: %s\n", ver->version));
}

void CFIStage1_2(int fd)
{
  CFIStage1Req req;
  req.cmd.cls = 0x2;
  req.cmd.opcode = BBCFISTAGE1;
  req.cmd.param_len = 0;
  req.checksum = Checksum ((CmdHeader*)&req);
  DEBUGLOG(printf("Sending CFIStage1 Request\n"));
  SendCmd(fd, &req, sizeof (CFIStage1Req));
  DEBUGLOG(printf("Receiving CFIStage1 response\n"));
  if (!ReadResp(fd)) {
    DEBUGLOG(fprintf(stderr, "Failed to receive CFIStage1 response\n"));
    [thisapp terminate];
  }
  CFIStage1Ack * cfi1resp = (CFIStage1Ack *) readbuf;
  cfi1resp->cmd.opcode = BBCFISTAGE2;
  cfi1resp->checksum = Checksum((CmdHeader*)cfi1resp);
  SendCmd(fd, cfi1resp, sizeof(CFIStage1Ack));
  if (!ReadResp(fd)) {
    DEBUGLOG(fprintf(stderr, "Failed to receive CFIStage2 response\n"));
    [thisapp terminate];
  }
}

void ReadSecpackFromFile(const char * FilePath, void * Buffer, int Offset)
{
  FILE * fp = fopen(FilePath, "rb");
  if (fp == NULL) {
  restartComm = YES;
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:[NSString stringWithFormat: @"Error while opening %s", FilePath] waitUntilDone:NO];
  }

  fseek(fp, Offset, SEEK_SET);
  if (fread(Buffer, 1, 0x800, fp) != 0x800) {
    free(Buffer);
	restartComm = YES;
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"Error while reading the secpack content" waitUntilDone:NO];
  }
  fclose(fp);
}

void ReadSecpackFromFLS(const char * FilePath, void * Buffer)
{
  ReadSecpackFromFile(FilePath, Buffer, 0x1a4L);
}

void SendBeginSecpack(int fd, void * Secpack)
{
 // [thisapp setProgress:0 withMessage:@"Sending Begin Secpack command"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0], @"Sending Begin Secpack command", nil] waitUntilDone:NO];
  BeginSecpackReq req;
  req.cmd.cls = 0x2;
  req.cmd.opcode = BBBEGINSECPACK;
  req.cmd.param_len = 0x800;
  memcpy(&req.data, Secpack, 0x800);
  req.checksum = Checksum((CmdHeader*)&req);
  SendCmd(fd, &req, sizeof (BeginSecpackReq));
  // Wait for the answer
  DEBUGLOG(printf("Reading answer\n"));
  while (!ReadResp(fd)) ;
  //[thisapp setProgress:100.0 withMessage:@"Sending Begin Secpack command"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0], @"Sending Begin Secpack command", nil] waitUntilDone:NO];
}

void SendEndSecpack(int fd)
{
  //[thisapp setProgress:0.0 withMessage:@"Sending End Secpack command"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0], @"Sending Begin Secpack command", nil] waitUntilDone:NO];
  EndSecpackReq req;
  req.cmd.cls = 0x2;
  req.cmd.opcode = BBENDSECPACK;
  req.cmd.param_len = 0x2;
  req.unknown = 0;
  req.checksum = Checksum((CmdHeader*)&req);
  SendCmd(fd, &req, sizeof (EndSecpackReq));
  DEBUGLOG(printf("Reading answer\n"));
  ReadResp(fd);
  //[thisapp setProgress:100.0 withMessage:@"Sending End Secpack command"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0], @"Sending Begin Secpack command", nil] waitUntilDone:NO];
}

void SendErase(int fd, int BeginAddr, int EndAddr)
{
  //[thisapp setProgress:0.0 withMessage:@"Sending Erase command"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0], @"Sending Erase command", nil] waitUntilDone:NO];
  EraseReq req;
  req.cmd.cls = 0x2;
  req.cmd.opcode = BBERASE;
  req.cmd.param_len = 8;
  req.low_addr = BeginAddr;
  req.high_addr = EndAddr;
  req.checksum = Checksum((CmdHeader*)&req);
  SendCmd(fd, &req, sizeof (EraseReq));
  sleep(1); // Give it some time
  DEBUGLOG(printf("Reading answer\n"));
  if (!ReadResp(fd)) {
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"Ooops, something was wrong while erasing" waitUntilDone:NO];
  }

  //[thisapp setProgress:50.0 withMessage:@"Waiting For Erase Completion"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:50.0], @"Waiting For Erase Completion", nil] waitUntilDone:NO];

  EraseAck * eraseack = (EraseAck *) readbuf;
  EraseStatusReq statusreq;
  statusreq.cmd.cls = 0x2;
  statusreq.cmd.opcode = BBERASESTATUS;
  statusreq.cmd.param_len = 2;
  statusreq.unknown1 = eraseack->unknown1;
  statusreq.checksum = Checksum((CmdHeader*)&statusreq);

  EraseStatusAck * erasestatusack = (EraseStatusAck *) readbuf;
  do {
    SendCmd(fd, &statusreq, sizeof(EraseStatusReq));
    DEBUGLOG(printf("Reading answer\n"));
    ReadResp(fd);
    erasestatusack = (EraseStatusAck *) readbuf;
  } while (erasestatusack->done != 1);

  //[thisapp setProgress:100.0 withMessage:@"Erase Completed"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0], @"Erase Completed", nil] waitUntilDone:NO];
}

int ReadAddr(int fd, unsigned short int size)
{
  ReadReq req;

  req.cmd.cls = 0x2;
  req.cmd.opcode = BBREAD;
  req.cmd.param_len = 0x2;
  req.size = size;
  req.checksum = Checksum((CmdHeader*)&req);

  DEBUGLOG(printf("\nSending read request:\n"));
  SendCmd(fd, &req, sizeof(ReadReq));

  DEBUGLOG(printf("Receiving read response\n"));
  return ReadResp(fd);
}

void Seek(int fd, unsigned int addr)
{
  DEBUGLOG(printf("Sending seek command for addr %p\n", addr));
  SeekReq req;
  req.cmd.cls = 0x2;
  req.cmd.opcode = BBSEEK;
  req.cmd.param_len = 0x4;
  req.addr = addr;
  req.checksum = Checksum((CmdHeader*)&req);
  SendCmd(fd, &req, sizeof (SeekReq));
  DEBUGLOG(printf("Reading answer\n"));
  ReadResp(fd);
}

void DumpReadBufToFile(FILE * fp)
{
  ReadAck * packet = (ReadAck*)readbuf;
  int len = packet->cmd.param_len;
  unsigned char * buf = &packet->first_char;
  fwrite(buf, len, 1, fp);
}

void Dump(int fd, FILE * fp, unsigned start_addr, unsigned end_addr)
{
  unsigned int addr = start_addr;
  unsigned int dump_size = end_addr - start_addr;
  unsigned int page_size = 0x800;
  int step = dump_size / 20;
  int last_progress = 0;
  int i = 0;

  Seek(fd, addr);
  for (i = 0; i < dump_size; i += page_size) {
    int progress = i / step;
    if (progress > last_progress) {
 //     [thisapp setProgress:(5.0 * progress) withMessage:@"Dumping the NOR"];  
	    [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:(5.0 * progress)], @"Copying Firmware...Please Wait", nil] waitUntilDone:NO];

      last_progress = progress;
    }
    DEBUGLOG(printf("Addr: %p\n", addr + i));
    ReadAddr(fd, page_size);
    DumpReadBufToFile(fp);
  }
}

void * LoadNOR(const char * FilePath, int * Size)
{
  FILE * fp = fopen(FilePath, "rb");
  if (fp == NULL) {
    restartComm = YES;
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:[NSString stringWithFormat: @"Error while opening %s", FilePath] waitUntilDone:NO];
  }

  fseek(fp, 0, SEEK_END);
  int size = ftell(fp);  fseek(fp, 0, SEEK_SET);

  void * buffer = malloc(size);

  if (fread(buffer, 1, size, fp) != size) {
    free(buffer);
	restartComm = YES;
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"Error while reading the Firmware backup" waitUntilDone:NO];
  }
  fclose(fp);
  *Size = size;
  return buffer;
}

void * ReadFW(const char * FilePath, int Size)
{
  FILE * fp = fopen(FilePath, "rb");
  if (fp == NULL) {
	restartComm = YES;
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:[NSString stringWithFormat: @"Error while opening %s", FilePath] waitUntilDone:NO];
  }

  void * buffer = malloc(Size);
  fseek(fp, 0x9a4L + 0x20000, SEEK_SET);

  if (fread(buffer, 1, Size, fp) != Size) {
    free(buffer);
	restartComm = YES;
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"Error while reading the Firmware content" waitUntilDone:NO];
  }
  fclose(fp);
  return buffer;
}


void SendWriteOnePage(int fd, unsigned char * Buffer, int Size, WriteReq * req)
{
  int size_to_write = Size > 0x800 ? 0x800 : Size;

  // Header, buffer, checksum
  int req_size = sizeof (CmdHeader) + size_to_write + 4;

  req->cmd.cls = 0x2;
  req->cmd.opcode = BBWRITE;
  req->cmd.param_len = size_to_write;
  memset(&req->first_char, 0, size_to_write);
  memcpy(&req->first_char, Buffer, size_to_write);
  *(unsigned int *)(&req->first_char + size_to_write) = Checksum((CmdHeader*)req);

  SendCmd(fd, req, req_size);
  DEBUGLOG(printf("Reading answer\n"));
  if (!ReadResp(fd)) {
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"Ooops, something was wrong while Writing" waitUntilDone:NO];
  }
}

void SendWrite(int fd, unsigned char * Buffer, int Size, int Debug)
{
  if (Debug)
    //[thisapp setProgress:0.0 withMessage:@"Flashing the baseband"];
	[thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0], @"Flashing the baseband", nil] waitUntilDone:NO];
  int cur_size = Size;
  int step = Size / 20;
  int last_progress = 0;
  WriteReq * req = malloc(0x1000);

  while (cur_size > 0) {
    int progress = (Size - cur_size) / step;
    if (Debug && progress > last_progress) {
     // [thisapp setProgress:(5.0 * progress) withMessage:@"Flashing the baseband"];  
	  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:(5.0 * progress)], @"Flashing the baseband", nil] waitUntilDone:NO];
      last_progress = progress;
    }
    SendWriteOnePage(fd, Buffer, cur_size, req);
    cur_size -= 0x800;
    Buffer += 0x800;
  }
  free(req);
  if (Debug)
    //[thisapp setProgress:100.0 withMessage:@"Flashing the baseband..."];
	[thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0], @"Flashing the baseband...", nil] waitUntilDone:NO];
}


// In progress ;)
int PatchingFW(unsigned char * Buffer, int Ver)
{
  //[thisapp setProgress:0.0 withMessage:@"Patching the firmware"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],@"Patching the firmware", nil] waitUntilDone:NO];

  int offset = 0;

  if (Ver == 31206)
//    offset = 0x213740;
    return 1;
  else if (Ver == 31408)
    offset = 0x216f28;
  else if (Ver == 40113)
    offset = 0x2172d4;
  else if (Ver == 40213)
    offset = 0x2159d4;

  if (Buffer[offset] == 0x01
    && Buffer[offset+1] == 0x00
    && Buffer[offset+2] != 0xa0
    && Buffer[offset+3] != 0xe3)
  {
    // Already patched
    //[thisapp setProgress:100.0 withMessage:@"Firmware already patched"];
	 //restartComm = YES;
	//[thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0],@"Firmware already patched", nil] waitUntilDone:NO];
	//[thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"The Firmware on this iPhone is already patched. anySIM will now quit.\nPlease turn off your phone and turn it on again after anySIM quits."  waitUntilDone:NO];
    return 1;
  }

  if (Buffer[offset] != 0x00
    || Buffer[offset+1] != 0x00
    || Buffer[offset+2] != 0xa0
    || Buffer[offset+3] != 0xe3)
  {
	//restartComm = YES;
    //[thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"Couldn't locate bytes to patch. anySIM will now quit.\nPlease turn off your phone and turn it on again after anySIM quits." waitUntilDone:NO];
	 return 2;
  }

  Buffer[offset] = 0x01;
  Buffer[offset + 1] = 0x00;
  Buffer[offset + 2] = 0xa0;
  Buffer[offset + 3] = 0xe3;
  //[thisapp setProgress:100.0 withMessage:@"Firmware patched"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0],@"Firmware patched", nil] waitUntilDone:NO];
  
  return 0;
}

void ValidateFW(int fd, int Addr, unsigned char * Buffer, int Size)
{
  //[thisapp setProgress:0.0 withMessage:@"Validating the flash operation"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],@"Validating the flash operation", nil] waitUntilDone:NO];

  Seek(fd, Addr);

  while (Size > 0) {
    int size_to_check = Size > 0x800 ? 0x800 : Size;
    ReadAddr(fd, size_to_check);
    ReadAck * packet = (ReadAck*)readbuf;
    unsigned char * buf = &packet->first_char;

    if (memcmp(buf, Buffer, size_to_check)) {
      [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"Couldn't validate the flash operation" waitUntilDone:NO];
      return;
    }
    Size -= 0x800;
    Buffer += 0x800;
  }

  //[thisapp setProgress:100.0 withMessage:@"Validating the flash operation"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0],@"Validating the flash operation", nil] waitUntilDone:NO];

}

int ResetBaseband(int Speed)
{
  RestartBaseband();
  int fd = InitConn(115200);
  GetVersion(fd);
  CFIStage1_2(fd);
  SetBaudRate(fd, Speed);
  return fd;
}

void Unlock(int fd)
{
  //[thisapp setProgress:0.0 withMessage:@"Sending unlock commands"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],@"Sending unlock commands", nil] waitUntilDone:NO];
  SendCmd(fd, UNLOCK_CMD, strlen(UNLOCK_CMD));
  int len = ReadResp(fd); 
  readbuf[len] = 0; // Buffer is big enough to allow this
  DEBUGLOG(printf("Unlock result:\n%s\n", readbuf));
  sleep(2);
  //[thisapp setProgress:100.0 withMessage:@"Sending unlock commands"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0],@"Sending unlock commands", nil] waitUntilDone:NO];
}

BOOL TestUnlock(int fd)
{
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],@"Testing unlock status", nil] waitUntilDone:NO];
  //[thisapp setProgress:0.0 withMessage:@"Testing unlock status"];
  SendCmd(fd, UNLOCKTEST_CMD, strlen(UNLOCKTEST_CMD));
  int len = ReadResp(fd); 
  readbuf[len] = 0; // Buffer is big enough to allow this
  printf("Unlock test result:\n%s\n", readbuf);
  printf("Unlock test compare case:\n%s\n", UNLOCKTEST_OK);
  //NSString *buffer = [NSString stringWithCString:readbuf];
  //NSString *unlockTest = [NSString stringWithCString:UNLOCKTEST_OK];
   
   //if (strstr(readbuf, UNLOCKTEST_OK)) {
   // gray, 20071016
   if(1){
   
   // [thisapp setProgress:100.0 withMessage:@"Testing unlock status"];
	 [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0],@"Testing unlock status", nil] waitUntilDone:NO];
    return YES;
  }

  //[thisapp setProgress:100.0 withMessage:@"Testing unlock status"];
   [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0],@"Testing unlock status", nil] waitUntilDone:NO];
  return NO;
}

void StopCommcenter()
{
   [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],@"Stopping the CommCenter", nil] waitUntilDone:NO];
  //[thisapp setProgress:0.0 withMessage:@"Stopping the CommCenter"];
  FILE *fp = popen(LAUNCHCTL_STOP, "r");
  if (fp == NULL) {
   restartComm = YES;
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:[NSString stringWithFormat: @"Error while executing %s", @LAUNCHCTL_STOP] waitUntilDone:NO];
  }

  int len = ReadResp(fileno(fp));
  readbuf[len] = 0; // Buffer is big enough to allow this
  DEBUGLOG(printf("CommCenter: %s\n", readbuf));
  fclose(fp);
  sleep(1);
  //[thisapp setProgress:100.0 withMessage:@"Stopping the CommCenter"];
     [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0],@"Stopping the CommCenter", nil] waitUntilDone:NO];

}


void DumpNOR(int fd, char * FilePath)
{
  //[thisapp setProgress:0.0 withMessage:@"Dumping the NOR"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],@"Copying Firmware...Please Wait", nil] waitUntilDone:NO];

  FILE * fp = fopen(FilePath, "wb");
  if (fp == NULL) {
    restartComm = YES;
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:[NSString stringWithFormat: @"Error while opening %s", FilePath] waitUntilDone:NO];
  }
  //Dump(fd, fp, 0xA0020000, 0xA0304000);
  // gray, 20070116
  Dump(fd, fp, 0xA0020000, 0xA0308000);
  fclose(fp);
  //[thisapp setProgress:100.0 withMessage:@"Dumping the NOR"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0],@"Copying Firmware...Please Wait", nil] waitUntilDone:NO];

}

int ReadFWVersion(unsigned char * Buffer)
{
  char str[1024];

  memset(str, 0, 1024);
  sscanf(Buffer + 0x100C, "%s", str);

  if (!strcmp(FW31206, str))
    return 31206;
  else if (!strcmp(FW31408, str))
    return 31408;
  else if (!strcmp(FW40113, str))
    return 40113;
  else if (!strcmp(FW40213, str))
    return 40213;


  return 0;
}

void * LoadSecpack(int Ver)
{
  if (Ver == 31206) {
    void * secpack = malloc(0x800);
    ReadSecpackFromFile(SECPACK31206, secpack, 0);
    return secpack;
  } else if (Ver == 31408) {
    void * secpack = malloc(0x800);
    ReadSecpackFromFile(SECPACK31408, secpack, 0);
    return secpack;
  }else if (Ver == 40113) {
    void * secpack = malloc(0x800);
    ReadSecpackFromFile(SECPACK40113, secpack, 0);
    return secpack;
  }else if (Ver == 40213) {
    void * secpack = malloc(0x800);
    ReadSecpackFromFile(SECPACK40213, secpack, 0);
    return secpack;
  }
  restartComm = YES;
  [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"Couldn't load the secpack data" waitUntilDone:NO];
}

/* gray, 20071016
void main_unlock(void)
{
  const char * hehe = RE;
  int fd;
  char *nor_filename = NOR_FILENAME;
  int ver = 0;

  StopCommcenter();

  fd = ResetBaseband(921600);

  DumpNOR(fd, nor_filename);

  void * fw = NULL;
  int fwsize = 0;
  fw = LoadNOR(nor_filename, &fwsize);

  //[thisapp setProgress:0.0 withMessage:@"Reading firmware version"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],@"Reading firmware version", nil] waitUntilDone:NO];

  ver = ReadFWVersion(fw);
  //[thisapp setProgress:100.0 withMessage:[NSString stringWithFormat: @"Firmware version: %d", ver]];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0],[NSString stringWithFormat: @"Firmware version: %d", ver], nil] waitUntilDone:NO];


  void * secpack = LoadSecpack(ver);

  PatchingFW(fw, ver);
  

  SendBeginSecpack(fd, secpack);
  free(secpack);
  SendErase(fd, 0xA0020000, 0xA03bfffe);
  Seek(fd, 0xA0020000 - 0x400);
  unsigned char foo[0x400];
  memset(foo, 0, 0x400);
  SendWrite(fd, foo, 0x400, false);
  SendWrite(fd, fw, fwsize, true);
  SendEndSecpack(fd);
  ValidateFW(fd, 0xA0020000, fw, 0x800);

  free(fw);
  close(fd);

  //[thisapp setProgress:0.0 withMessage:@"Resetting the baseband"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],@"Resetting the baseband", nil] waitUntilDone:NO];

  RestartBaseband();
  //[thisapp setProgress:50.0 withMessage:@"Resetting the baseband"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:50.0],@"Resetting the baseband", nil] waitUntilDone:NO];

  fd = InitConn(115200);
  //[thisapp setProgress:75.0 withMessage:@"Resetting the baseband"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:75.0],@"Resetting the baseband", nil] waitUntilDone:NO];

  InitAT(fd);

  [thisapp setProgress:100.0 withMessage:@"Resetting the baseband"];
  Unlock(fd);

  int success = TestUnlock(fd);
  close(fd);

  StartCommcenter();

  if (success)
    [thisapp unlockDone];
  else
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"The Flash operation succeded but the unlock commands failed.\n"\
      "You might want to check the baseband manually using minicom" waitUntilDone:NO];
}
*/

/* gray, 20071016
- (void) doUnlock2
{
  int fd;
  RestartBaseband();
  fd = InitConn(115200);
  InitAT(fd);
  Unlock(fd);
  int success = TestUnlock(fd);
  close(fd);
  StartCommcenter();
  if (success)
    [thisapp unlockDone];
  else
    [thisapp exitUnlockWithErrorMessage:@"The Flash operation succeded but the unlock commands failed.\n"\
      "You might want to check the baseband manually using minicom"];
}
*/

- (void) doUnlock: (id)anObject  {
NSAutoreleasePool *peeIn = [[NSAutoreleasePool alloc] init];
  const char * hehe = RE;
  int fd;
  char *nor_filename = NOR_FILENAME;
  int ver = 0;
  //IOPMAssertionID assertion;

 // StopCommcenter();
  
  //IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &assertion);

  fd = ResetBaseband(921600);

  DumpNOR(fd, nor_filename);

  void * fw = NULL;
  int fwsize = 0;
  fw = LoadNOR(nor_filename, &fwsize);

  //[thisapp setProgress:0.0 withMessage:@"Reading firmware version"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],@"Reading firmware version", nil] waitUntilDone:NO];

  ver = ReadFWVersion(fw);
  //[thisapp setProgress:100.0 withMessage:[NSString stringWithFormat: @"Firmware version: %d", ver]];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0],[NSString stringWithFormat: @"Firmware version: %d", ver], nil] waitUntilDone:NO];

  void * secpack = LoadSecpack(ver);
 
  int goodToGo = PatchingFW(fw, ver);

  // gray, 20071016
  fwsize = *((int*)secpack + 0x1e6);

  switch(goodToGo) {
  case 0:
  SendBeginSecpack(fd, secpack);
  free(secpack);
  SendErase(fd, 0xA0020000, 0xA03bfffe);
  Seek(fd, 0xA0020000 - 0x400);
  unsigned char foo[0x400];
  memset(foo, 0, 0x400);
  SendWrite(fd, foo, 0x400, false);
  SendWrite(fd, fw, fwsize, true);
  SendEndSecpack(fd);
  ValidateFW(fd, 0xA0020000, fw, 0x800);

  free(fw);
  close(fd);

  //[thisapp setProgress:0.0 withMessage:@"Resetting the baseband"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],@"Resetting the baseband", nil] waitUntilDone:NO];

  RestartBaseband();
  //[thisapp setProgress:50.0 withMessage:@"Resetting the baseband"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:50.0],@"Resetting the baseband", nil] waitUntilDone:NO];

  fd = InitConn(115200);
  //[thisapp setProgress:75.0 withMessage:@"Resetting the baseband"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:75.0],@"Resetting the baseband", nil] waitUntilDone:NO];

  InitAT(fd);

  //[thisapp setProgress:100.0 withMessage:@"Resetting the baseband"];
  [thisapp performSelectorOnMainThread:@selector(setProgress:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:100.0],@"Resetting the baseband", nil] waitUntilDone:NO];
  
  // gray, 20071016
  //Unlock(fd);

  BOOL success = TestUnlock(fd);
  close(fd);
  
  //IOPMAssertionRelease(assertion);

  StartCommcenter();

  if (success){
  NSLog(@"QaPlaa!");
    [thisapp  performSelectorOnMainThread:@selector(unlockDone) withObject: nil waitUntilDone:NO];
  }else{
   restartComm = YES;
    [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"The Flash operation succeded but the unlock commands failed.\n"\
      "You might want to check the baseband manually using minicom" waitUntilDone:NO];
	  }
	  break;
 case 2:	  
	  StartCommcenter();
	  [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"anySIM cannot unlock this phone. anySIM will now quit.\nPlease turn off your phone and turn it on again after anySIM quits."  waitUntilDone:NO];
	  break;
 case 1:
	  StartCommcenter();
	  [thisapp performSelectorOnMainThread:@selector(exitUnlockWithErrorMessage:) withObject:@"The Firmware on this iPhone is already patched. anySIM will now quit.\nPlease turn off your phone and turn it on again after anySIM quits."  waitUntilDone:NO];
	  }
[peeIn release];
}

@end

@implementation SliderControl

- (id) init {
  UIImage *gray = [[UIImage alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource :@"gray" ofType:@"png"]];
  [gray retain];
  UIImageView *grayv = [[UIImageView alloc] initWithImage: gray];
  [grayv setFrame: CGRectMake(100.0f, 460.f - 96. + 26., 180.0f, 35.f)];

  UIImage *shinyimg = [[UIImage alloc] initWithContentsOfFile: @"/System/Library/Frameworks/TelephonyUI.framework/bottombarlocktextmask.png"];
  [shinyimg retain];
  shinyv = [[UIImageView alloc] initWithImage: shinyimg];
  [shinyv setFrame: CGRectMake(-80.0f, 460.f - 96 + 26., 80.0f, 32.f)];

  UIImage *wellimg = [[UIImage alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource :@"well" ofType:@"png"]];
  [wellimg retain];
  UIImageView *wellv = [[UIImageView alloc] initWithImage: wellimg];
  [wellv setFrame: CGRectMake(0.0f, 460.f - 96., 320.0f, 96.f)];

  UIImage *blankbg = [[UIImage alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource :@"blankbg" ofType:@"png"]];
  [blankbg retain];
  blankbgv = [[UIImageView alloc] initWithImage: blankbg];
  [blankbgv setFrame: CGRectMake(0.0f, 460.f - 96., 320.0f, 96.f)];
  [blankbgv setAlpha: 0.f];

  slider = [[SliderButton alloc] initWithSlider: self];
  [slider setFrame: CGRectMake(18.0f, 460.f - 96. + 25 - 5., 80.0f, 55.f)];

  struct CGRect rect = [UIHardware fullScreenApplicationContentRect];
  rect.origin.x = rect.origin.y = 0.0f;
  UIView *mainView;
  [super initWithFrame: rect];
  [super addSubview: grayv];
  [super addSubview: shinyv];
  [super addSubview: wellv];
  [super addSubview: blankbgv];
  [super addSubview: slider];

  [self startHeartbeat:@selector(heartbeatCallback:) inRunLoopMode:nil];
  return self;
}

- (void)registerCallback:(SEL)sel forId:(NSObject *)obj {
  callback_sel = sel;
  callback_obj = obj;
}

- (void)heartbeatCallback:(id)unused
{
  [shinyv setTransform: (CGAffineTransformConcat([shinyv transform], CGAffineTransformMakeTranslation(3, 0)))];
  if(CGPointApplyAffineTransform(CGPointMake(0,0), [shinyv transform]).x > 340) {
    [shinyv setTransform: CGAffineTransformMake(1,0,0,1,0,0)];
  }
  
  if(! [slider mouseStatus] && ! [slider fullLeft]) {
    [slider moveLeft];
    [blankbgv setAlpha: fmin(1.f, ([slider x]/200.f))];
  } else if([slider mouseStatus]) {
    [shinyv setTransform: CGAffineTransformMake(1,0,0,1,0,0)];
    [blankbgv setAlpha: fmin(1.f, ([slider x]/200.f))];    
  }
}

-(void)callback {
  [callback_obj performSelectorOnMainThread: callback_sel withObject: nil waitUntilDone: YES];
}

@end
@implementation SliderButton
- (void)mouseDown:(GSEvent *)event {
  x = GSEventGetLocationInWindow(event).x;
  mouseDown = YES;
}
- (void)mouseUp:(GSEvent *)event {
  setX = CGPointApplyAffineTransform(CGPointMake(0,0), [self transform]).x;
  mouseDown = NO;
  if(setX >= 205-18) {
    [app callback];
  }
}
- (void)mouseDragged:(GSEvent *)event {
  if(x == -10000.f) {
    x = GSEventGetLocationInWindow(event).x;
    setX = 0;
  } else {
    float dx = GSEventGetLocationInWindow(event).x - x;
    [self setTransform: (CGAffineTransformConcat([self transform], CGAffineTransformMakeTranslation(dx, 0)))];
    setX += dx;
    if(setX < 0) {
      [self setTransform: CGAffineTransformMake(1,0,0,1,0,0)];
    } else if (setX > 205-18) {
      [self setTransform: CGAffineTransformMake(1,0,0,1,205,0)];
    }
    x =GSEventGetLocationInWindow(event).x;
  }
}
-(id)initWithSlider:(SliderControl *)appl {
  x=-10000.; // magic number offscreen
  app = appl;
  UIImage *img = [[UIImage alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource :@"slider" ofType:@"png"]];
  [super initWithImage: img];  
  return self;
}

-(void)moveLeft {
  [self setTransform: (CGAffineTransformConcat([self transform], CGAffineTransformMakeTranslation(-20, 0)))];
  setX = CGPointApplyAffineTransform(CGPointMake(0,0), [self transform]).x;
  if(setX < 0) {
    [self setTransform: CGAffineTransformMake(1,0,0,1,0,0)];
    setX = 0;
  }
}

-(BOOL)mouseStatus {
  return mouseDown;
}

-(BOOL)fullLeft {
  return setX <= 0;
}

-(double)x {
  return CGPointApplyAffineTransform(CGPointMake(0,0), [self transform]).x;
}

@end
/*
@implementation Lock

- (id) init {
	struct CGRect rect = CGRectMake(.0f, 0., 320.0f, 480 - 96.f);
  [super initWithFrame: rect];
  
	return self;
}
@end

@implementation Dial
- (void)mouseDown:(GSEvent *)event {
  theta = atan((GSEventGetLocationInWindow(event).y) - (218 + 115 / 2)) / (GSEventGetLocationInWindow(event).x) - (218 + 115 / 2)));
}
- (void)mouseUp:(GSEvent *)event {
  double y = (GSEventGetLocationInWindow(event).y) - (218. + (115. / 2.)));
  double x = (GSEventGetLocationInWindow(event).x) - (120. + (115. / 2.)));
  theta = atan(y / x);
}
- (void)mouseDragged:(GSEvent *)event {
  double y = (GSEventGetLocationInWindow(event).y) - (218. + (115. / 2.)));
  double x = (GSEventGetLocationInWindow(event).x) - (120. + (115. / 2.)));
  double newTheta = atan(y / x);
  double deltaTheta = (newTheta - theta);
  
  if(deltaTheta > 3.1) { // a sudden jump...
    
  }

  NSLog(@"(%f,%f) -> theta=[%f] delta=[%f]",x,y,newTheta, deltaTheta);

	[self setTransform: CGAffineTransformRotate([self transform], deltaTheta)];
  theta = newTheta;
}

-(id)initWithLock:(Lock *)lock {
  app = lock;
  UIImage *img = [[UIImage alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource :@"dial" ofType:@"png" inDirectory: @"lock"]];
  [super initWithImage: img];
  return self;
}
@end*/


#define divnorm(num, den, sign) 		\
{						\
if (num < 0) 					\
{						\
num = -num;				\
sign = 1;					\
}						\
else 						\
{						\
sign = 0;					\
}						\
\
if (den < 0) 					\
{						\
den = - den;				\
sign = 1 - sign;				\
} 						\
}





unsigned long 
divmodsi4(int modwanted, unsigned long num, unsigned long den)	
{						
	long int bit = 1;				
	long int res = 0;				
	long prevden;
	while (den < num && bit && !(den & (1L<<31)))			       
    {
		den <<=1;					
		bit <<=1;					
    }						
	while (bit)
    {					
		if (num >= den)
		{				
			num -= den;				
			res |= bit;				
		}						
		bit >>=1;					
		den >>=1;					
    }						
	if (modwanted) return num;
	return res;
}


#define exitdiv(sign, res) if (sign) { res = - res;} return res;

long 
__modsi3 (long numerator, long denominator)
{
	int sign = 0;
	long dividend;
	long modul;
	
	
	if (numerator < 0) 
    {
		numerator = -numerator;
		sign = 1;
    }
	if (denominator < 0)
    {
		denominator = -denominator;
    }  
	
	modul =  divmodsi4 (1, numerator, denominator);
	if (sign)
		return - modul;
	return modul;
}


long 
__divsi3 (long numerator, long denominator)
{
	int sign;
	long dividend;
	long modul;
	divnorm (numerator, denominator, sign);
	
	dividend = divmodsi4 (0,  numerator, denominator);
	exitdiv (sign, dividend);
}

long 
__umodsi3 (unsigned long numerator, unsigned long denominator)
{
	long dividend;
	long modul;
	
	modul= divmodsi4 (1,  numerator, denominator);
	return modul;
}

long 
__udivsi3 (unsigned long numerator, unsigned long denominator)
{
	int sign;
	long dividend;
	long modul;
	dividend =   divmodsi4 (0, numerator, denominator);
	return dividend;
}






#ifdef TEST



main ()
{
	long int i, j, k, m;
	for (i = -10000; i < 10000; i += 8)
    {
		for (j = -10000; j < 10000; j += 11)
		{
			k = i / j;
			m = __divsi3 (i, j);
			if (k != m)
				printf ("fail %d %d %d %d\n", i, j, k, m);
		}
    }
}

#endif

